import Foundation

// Converts an Int32 value to a byte array in little-endian order.
// The byte array contains 4 elements, one for each byte of the Int32 value.
//
// - Parameter value: The Int32 value to convert (e.g., 32-bit integer).
// - Returns: A byte array ([UInt8]) representing the little-endian byte order of the input value.
//
// Example:
//   let byteArray = int32ToByteArray(value: 12345)
//   print(byteArray)  // Output: [57, 48, 0, 0]
func int32ToByteArray(value: Int32) -> [UInt8] {
    var result: [UInt8] = []
    
    // Loop through each of the 4 bytes and extract the appropriate byte.
    for i in 0..<4 {
        let byte = UInt8(truncatingIfNeeded: value >> (8 * i))
        result.append(byte)
    }
    
    return result
}

// Converts a byte array (little-endian order) back to an Int32 value.
// If the byte array has fewer than 4 elements, it is padded with zeros on the left.
//
// - Parameter bytes: The byte array to convert, limited to 4 bytes or less.
// - Returns: The resulting Int32 value corresponding to the byte array.
//
// Example:
//   let value = byteArrayToInt32(bytes: [57, 48, 0, 0])
//   print(value)  // Output: 12345
func byteArrayToInt32(bytes: [UInt8]) -> Int32 {
    // Take at most 4 bytes from the right side (little-endian order).
    let relevantBytes = bytes.suffix(4)
    
    // Pad with zeros on the left if fewer than 4 bytes are provided.
    let paddedBytes = [UInt8](repeating: 0, count: 4 - relevantBytes.count) + relevantBytes
    
    var result: Int32 = 0
    // Combine the bytes into a single Int32 value by shifting each byte to its correct position.
    for (index, byte) in paddedBytes.enumerated() {
        result |= Int32(byte) << (8 * (3 - index)) // Shift byte to its proper position.
    }
    
    return result
}

// Calculates the CRC16 Modbus checksum for the input data array.
// The function uses a polynomial of 0xA001 and starts with an initial value of 0xFFFF.
//
// - Parameter input: The input data array ([UInt8]) to calculate the CRC16 checksum for.
// - Returns: A byte array containing the two bytes of the CRC16 checksum in little-endian order.
//
// Example:
//   let data: [UInt8] = [0x01, 0x03, 0x00, 0x00, 0x00, 0x02]
//   let crc = calculateCRC16Modbus(input: data)
//   print(crc)  // Output: [0xC2, 0xC4] (CRC value in little-endian)
func calculateCRC16Modbus(input: [UInt8]) -> [UInt8] {
    var crc: UInt16 = 0xFFFF
    
    // Loop through each byte in the input array and process the CRC.
    for byte in input {
        crc ^= UInt16(byte)
        
        // Perform 8 shifts for each byte, applying the CRC algorithm.
        for _ in 0..<8 {
            if crc & 0x0001 != 0 {
                crc >>= 1
                crc ^= 0xA001
            } else {
                crc >>= 1
            }
        }
    }
    
    // Convert the CRC value into a two-byte array (little-endian order).
    let crcBytes: [UInt8] = [
        UInt8(crc & 0xFF),       // Low byte
        UInt8((crc >> 8) & 0xFF) // High byte
    ]
    
    return crcBytes
}

// Computes the XOR checksum of the provided data array.
// The XOR checksum is calculated by XOR-ing each byte in the array.
//
// - Parameter data: The data array ([UInt8]) to calculate the XOR checksum for.
// - Returns: A single byte (UInt8) representing the XOR checksum.
//
// Example:
//   let data: [UInt8] = [0x01, 0x02, 0x03]
//   let checksum = XorCheckSum(data: data)
//   print(checksum)  // Output: 0x00 (XOR checksum of the data)
func XorCheckSum(data: [UInt8]) -> UInt8 {
    let length = data.count
    var checksum: UInt8 = 0
    
    // XOR all bytes in the data array.
    for i in 0..<length {
        checksum ^= data[i]
    }
    
    return checksum
}

// Converts a byte array to a string, assuming UTF-8 encoding.
// Returns an empty string if the conversion fails.
//
// - Parameter byteArray: The byte array ([UInt8]) to convert to a string.
// - Returns: The resulting string (String) from the byte array, or an empty string if conversion fails.
//
// Example:
//   let byteArray: [UInt8] = [72, 101, 108, 108, 111]
//   let str = uInt8ToString(byteArray: byteArray)
//   print(str)  // Output: "Hello"
func uInt8ToString(byteArray: [UInt8]) -> String {
    return String(bytes: byteArray, encoding: .utf8) ?? ""
}
